package com.frank.globalException.advice;

import com.frank.globalException.domain.BaseResponse;
import com.frank.globalException.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

/**
 * @author 小石潭记
 * @date 2020/10/22 9:49
 * @Description: 1.新建一个统一异常处理的一个类
 * 2.类上标注@RestControllerAdvice这一个注解，或者同时标注@ControllerAdvice和@ResponseBody这两个注解。
 * 3.在方法上标注@ExceptionHandler注解，并且指定需要捕获的异常，可以同时捕获多个。
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {

    /**
     * 抛出该自定义异常就会执行该方法，可以根据实际的业务进行扩展
     * value里面可以绑定多个异常 value={Main.class, B.class}
     */
    @ExceptionHandler(value = MyException.class)
    public BaseResponse handleMyException(MyException e, HttpServletRequest request) {
        return BaseResponse.fail(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String defaultExceptionHandler(Exception ex) {
        ex.printStackTrace();
        if (ex instanceof BindException) {
            //处理返回的错误信息
            StringBuffer errorMsg = new StringBuffer();
            BindException c = (BindException) ex;
            List<ObjectError> errors = c.getBindingResult().getAllErrors();
            for (ObjectError error : errors) {
                errorMsg.append(error.getDefaultMessage()).append(";");
            }
            return errorMsg.toString();

        }
        return "";

    }

    // 捕获方法参数校验异常
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResponseEntity constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> message = e.getConstraintViolations();
        HashMap<String, Object> map = new HashMap<>();
        message.stream().forEach(msg -> {
            String path = msg.getPropertyPath().toString();
            String field = path.substring(path.indexOf(".") + 1);
            map.put(field, msg.getMessageTemplate());
        });
        return ResponseEntity.ok(map);
    }

    // 捕获实体参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseEntity resolveMethodArgumentNotValidException(MethodArgumentNotValidException e){
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        HashMap<String, Object> map = new HashMap<>();
        allErrors.stream().forEach(error -> {
            FieldError fieldError = (FieldError) error;
            map.put(fieldError.getField(), fieldError.getDefaultMessage());
        });
        return ResponseEntity.ok(map);
    }

    /**
     * 判断哪些需要拦截 return true 就会执行beforeBodyWrite
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * 经过decoder之后，就会到达这里，返回结果前，可以对结果进行处理
     */
    @Override
    public Object beforeBodyWrite(Object obj, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        // System.out.println("==>beforeBodyWrite:" + obj + "," + methodParameter);
        return obj;
    }

}
